package com.open4cn.simulator.connect.com;

import com.open4cn.simulator.core.connector.AbstractConnector;
import com.open4cn.simulator.core.connector.ConnectAddress;
import com.open4cn.simulator.core.connector.ConnectorModeEnum;
import com.open4cn.simulator.core.connector.serialport.SerialPortBootstrap;
import com.open4cn.simulator.core.connector.serialport.SerialPortChannel;
import com.open4cn.simulator.core.connector.serialport.SerialPortInboundHandler;
import com.open4cn.simulator.core.connector.serialport.SerialPortInitializer;
import com.open4cn.simulator.core.connector.serialport.SerialPortOpenListener;
import com.open4cn.simulator.core.connector.serialport.SerialPortOutboundHandler;
import com.open4cn.simulator.core.exception.IllegalParameterException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author yinxiaopeng
 * @version 1.0.0
 * @company 致景科技
 * @department 飞梭BU/研发
 * @description
 * @Email yinxiaopeng@zj.tech
 * @date 2022-06-10 18:21
 */
public class SerialPortConnector extends AbstractConnector {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    public SerialPortConnector() {
        super(ConnectorModeEnum.SERIAL_PORT);
    }

    private SerialPortChannel serialPortChannel;
    private SerialPortBootstrap serialPortBootstrap;

    // 入站处理器
    private List<SerialPortInboundHandler> beforeDecodHandlers = new ArrayList<>();

    private List<SerialPortInboundHandler> decoderHandlers = new ArrayList<>();

    private List<SerialPortInboundHandler> afterDecodHandlers = new ArrayList<>();

    // 出站处理器
    private List<SerialPortOutboundHandler> beforeEncodHandlers = new ArrayList<>();

    private List<SerialPortOutboundHandler> encoderHandlers = new ArrayList<>();

    private List<SerialPortOutboundHandler> afterEncodHandlers = new ArrayList<>();

    private List<SerialPortOpenListener> openListeners = new ArrayList<>();


    public <T extends SerialPortInboundHandler> void beforeDecod(T serialPortInboundHandler) {
        if (!beforeDecodHandlers.contains(serialPortInboundHandler)) {
            beforeDecodHandlers.add(serialPortInboundHandler);
        }
    }

    public <T extends SerialPortInboundHandler> void decoder(T serialPortInboundHandler) {
        if (!decoderHandlers.contains(serialPortInboundHandler)) {
            decoderHandlers.add(serialPortInboundHandler);
        }
    }

    public <T extends SerialPortInboundHandler> void afterDecod(T serialPortInboundHandler) {
        if (!afterDecodHandlers.contains(serialPortInboundHandler)) {
            afterDecodHandlers.add(serialPortInboundHandler);
        }
    }

    public <E extends SerialPortOutboundHandler> void beforeEncod(E serialPortOutboundHandler) {
        if (!beforeEncodHandlers.contains(serialPortOutboundHandler)) {
            beforeEncodHandlers.add(serialPortOutboundHandler);
        }
    }

    public <E extends SerialPortOutboundHandler> void encoder(E serialPortOutboundHandler) {
        if (!encoderHandlers.contains(serialPortOutboundHandler)) {
            encoderHandlers.add(serialPortOutboundHandler);
        }
    }

    public <E extends SerialPortOutboundHandler> void afterEncod(E serialPortOutboundHandler) {
        if (!afterEncodHandlers.contains(serialPortOutboundHandler)) {
            afterEncodHandlers.add(serialPortOutboundHandler);
        }
    }

    public <F extends SerialPortOpenListener> void addOpenListenner(F serialPortOpenListener) {
        if (!openListeners.contains(serialPortOpenListener)) {
            openListeners.add(serialPortOpenListener);
        }
    }


    @Override
    public void connect(ConnectAddress address) {
        close();
        if (!address.check(getConnectorMode())) {
            throw new IllegalParameterException(String.format("连接地址非法:%s", address.toString()));
        }
        setConnectAddress(address);
        serialPortBootstrap = new SerialPortBootstrap()
                .timeout(120)
                .threadPool(new ThreadPoolExecutor(1, 2, 60, TimeUnit.SECONDS, new LinkedBlockingQueue()))
                .handler(new SerialPortInitializer() {
                    @Override
                    protected void initSerialPort() {
                        // 入站处理器
                        if (!beforeDecodHandlers.isEmpty()) {
                            beforeDecodHandlers.forEach(serialPortInboundHandler -> {
                                logger.info("注册handler:{}", serialPortInboundHandler.getClass().getName());
                                addLast(serialPortInboundHandler);
                            });
                        }
                        if (!decoderHandlers.isEmpty()) {
                            decoderHandlers.forEach(serialPortInboundHandler -> {
                                logger.info("注册handler:{}", serialPortInboundHandler.getClass().getName());
                                addLast(serialPortInboundHandler);
                            });
                        }
                        if (!afterDecodHandlers.isEmpty()) {
                            afterDecodHandlers.forEach(serialPortInboundHandler -> {
                                logger.info("注册handler:{}", serialPortInboundHandler.getClass().getName());
                                addLast(serialPortInboundHandler);
                            });
                        }

                        // 出站处理器
                        if (!beforeEncodHandlers.isEmpty()) {
                            beforeEncodHandlers.forEach(serialPortOutboundHandler -> {
                                logger.info("注册handler:{}", serialPortOutboundHandler.getClass().getName());
                                addLast(serialPortOutboundHandler);
                            });
                        }
                        if (!encoderHandlers.isEmpty()) {
                            encoderHandlers.forEach(serialPortOutboundHandler -> {
                                logger.info("注册handler:{}", serialPortOutboundHandler.getClass().getName());
                                addLast(serialPortOutboundHandler);
                            });
                        }
                        if (!afterEncodHandlers.isEmpty()) {
                            afterEncodHandlers.forEach(serialPortOutboundHandler -> {
                                logger.info("注册handler:{}", serialPortOutboundHandler.getClass().getName());
                                addLast(serialPortOutboundHandler);
                            });
                        }
                    }
                });
        serialPortChannel = serialPortBootstrap.open(address.getComPort(), address.getBaudRate());

        // 串口打开之后的监听
        if (!openListeners.isEmpty()) {
            openListeners.forEach(openListener -> {
                openListener.operationComplete(serialPortChannel);
            });
        }
    }

    @Override
    public boolean close() {
        if (isClosed()) {
            return true;
        }
        if (null != serialPortBootstrap) {
            serialPortBootstrap.shutdown();
        }
        if (null != serialPortChannel) {
            serialPortChannel.close();
        }
        return beClosed();
    }

    @Override
    public void destroy() {
        if (isClosed()) {
            serialPortChannel = null;
            serialPortBootstrap = null;
        }
    }

    @Override
    public boolean send(Object msg) {
        if (isConnected() && null != serialPortChannel) {
            serialPortChannel.write(msg.toString().getBytes());
            return true;
        }
        return false;
    }
}
